home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / quicktime / quicktimeintro / wiredsprites / completed lab / qtwiredsprites.c next >
Encoding:
Text File  |  2000-10-06  |  31.8 KB  |  742 lines

  1. //////////
  2. //
  3. //    File:        QTWiredSprites.c
  4. //
  5. //    Contains:    QuickTime wired sprites support for QuickTime movies.
  6. //
  7. //    Written by:    Sean Allen
  8. //    Revised by:    Chris Flick and Tim Monroe
  9. //                Based (heavily!) on the existing MakeActionSpriteMovie.c code written by Sean Allen.
  10. //
  11. //    Copyright:    © 1997-1998 by Apple Computer, Inc., all rights reserved.
  12. //
  13. //    Change History (most recent first):
  14. //
  15. //       <5>         03/20/00    rtm        made changes to get things running under CarbonLib
  16. //       <4>         07/30/99    rtm        added QTWired_AddCursorChangeOnMouseOver to illustrate new
  17. //                                    sprite-as-button behaviors added in QT4; added this action to penguin one
  18. //       <3>         09/30/98    rtm        tweaked call to AddMovieResource to create single-fork movies
  19. //       <2>         03/26/98    rtm        made fixes for Windows compiles
  20. //       <1>         03/25/98    rtm        first file; integrated existing code with shell framework
  21. //       
  22. //
  23. //    This sample code creates a wired sprite movie containing one sprite track. The sprite track contains
  24. //    six sprites: two penguins and four buttons.
  25. //    
  26. //    The four buttons are initially invisible. When the mouse enters (or "rolls over") a button, it appears.
  27. //    When the mouse is clicked inside a button, its image changes to its "pressed" image. When the mouse
  28. //    is released, its image changes back to its "unpressed" image. If the mouse is released inside the button, 
  29. //    an action is triggered. The buttons perform the actions of go to beginning of movie, step backward,
  30. //    step forward, and go to end of movie.
  31. //    
  32. //    The first penguin shows all of the buttons when the mouse enters it, and hides them when the mouse exits.
  33. //    The first penguin is the only sprite that has properties that are overriden by the override sprite samples.
  34. //    These samples override its matrix (in order to move it) and its image index (in order to make it "waddle").
  35. //    
  36. //    When the mouse is clicked on the second penguin, it changes its image index to it's "eyes closed" image.
  37. //    When the mouse is released, it changes back to its normal image. This makes it appear to blink when clicked on.
  38. //    When the mouse is released over the penguin, several actions are triggered. Both penguins' graphics states are 
  39. //    toggled between copyMode and blendMode, and the movie's rate is toggled between zero and one.
  40. //    
  41. //    The second penguin moves once per second. This occurs whether the movie's rate is currently zero or one,
  42. //    because it is being triggered by a gated idle event. When the penguin receives the idle event, it changes
  43. //    its matrix using an action which uses min, max, delta, and wraparound options.
  44. //
  45. //    The movie's looping mode is set to palindrome by a frame-loaded action.
  46. //
  47. //    So, our general strategy is as follows (though perhaps not in the order listed):
  48. //
  49. //        (1) Create a new movie file with a single sprite track.
  50. //        (2) Assign the "no controller" movie controller to the movie.
  51. //        (3) Set the sprite track's background color, idle event frequency, and hasActions properties.
  52. //        (4) Convert our PICT resources to animation codec images with transparency.
  53. //        (5) Create a key frame sample containing six sprites and all of their shared images.
  54. //        (6) Assign the sprites their initial property values.
  55. //        (7)    Create a frameLoaded event for the key frame.
  56. //        (8)    Create some override samples that override the matrix and image index properties of
  57. //            the first penguin sprite.
  58. //
  59. //    NOTES:
  60. //        
  61. //    *** (1) ***
  62. //    There are event types other that mouse related events (for instance, Idle and FrameLoaded).
  63. //    Idle events are independent of the movie's rate, and they can be gated so they are send at most
  64. //    every n ticks. In our sample movie, the second penguin moves when the movie's rate is zero,
  65. //    and moves only once per second because of the value of the sprite track's idleEventFrequencey property.
  66. //        
  67. //    *** (2) ***
  68. //    Multiple actions may be executed in response to a single event. In our sample movie, rolling over
  69. //    the first penguin shows and hides four different buttons.
  70. //        
  71. //    *** (3) ***
  72. //    Actions may target any sprite or track in the movie. In our sample movie, clicking on one penguin
  73. //    changes the graphics mode of the other.
  74. //        
  75. //    *** (4) ***
  76. //    Conditional and looping control structures are supported. In our sample movie, the second penguin
  77. //    uses the "case statement" action.
  78. //        
  79. //    *** (5) ***
  80. //    Sprite track variables that have not been set have a default value of zero. (The second penguin's
  81. //    conditional code relies on this.)
  82. //        
  83. //    *** (6) ***
  84. //    Wired sprites were previously known as "action sprites". Don't let the names of some of the utility
  85. //    functions confuse you. We'll try to update the source code as time permits.
  86. //        
  87. //    *** (7) ***
  88. //    Penguins don't fly, but I hear they totally shred halfpipes on snowboards.
  89. //
  90. //////////
  91.  
  92.  
  93. // header files
  94. #include "QTWiredSprites.h"
  95.  
  96.  
  97. //////////
  98. //
  99. // QTWired_CreateWiredSpritesMovie
  100. // Create a QuickTime movie containing a wired sprites track.
  101. //
  102. //////////
  103.  
  104. OSErr QTWired_CreateWiredSpritesMovie (void)
  105. {
  106.     short                    myResRefNum = 0;
  107.     short                    myResID = movieInDataForkResID;
  108.     Movie                    myMovie = NULL;
  109.     Track                    myTrack;
  110.     Media                    myMedia;
  111.     FSSpec                    myFile;
  112.     Boolean                    myIsSelected = false;
  113.     Boolean                    myIsReplacing = false;    
  114.     StringPtr                 myPrompt = QTUtils_ConvertCToPascalString(kWiredSavePrompt);
  115.     StringPtr                 myFileName = QTUtils_ConvertCToPascalString(kWiredSaveFileName);
  116.     QTAtomContainer            mySample = NULL;
  117.     QTAtomContainer            myActions = NULL;
  118.     QTAtomContainer            myBeginButton, myPrevButton, myNextButton, myEndButton;
  119.     QTAtomContainer            myPenguinOne, myPenguinTwo, myPenguinOneOverride;
  120.     QTAtomContainer            myBeginActionButton, myPrevActionButton, myNextActionButton, myEndActionButton;
  121.     QTAtomContainer            myPenguinOneAction, myPenguinTwoAction;
  122.     RGBColor                myKeyColor;
  123.     Point                    myLocation;
  124.     short                    isVisible, myLayer, myIndex, myID, i, myDelta;
  125.     Boolean                    hasActions;
  126.     long                    myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
  127.     OSType                    myType = FOUR_CHAR_CODE('none');
  128.     UInt32                    myFrequency;
  129.     QTAtom                    myEventAtom;
  130.     long                    myLoopingFlags;
  131.     ModifierTrackGraphicsModeRecord        myGraphicsMode;
  132.     OSErr                    myErr = noErr;
  133.  
  134.     //////////
  135.     //
  136.     // create a new movie file and set its controller type
  137.     //
  138.     //////////
  139.  
  140.     // ask the user for the name of the new movie file
  141.     QTFrame_PutFile(myPrompt, myFileName, &myFile, &myIsSelected, &myIsReplacing);
  142.     if (!myIsSelected)
  143.         goto bail;
  144.  
  145.  
  146.     // create a movie file for the destination movie
  147.     myErr = CreateMovieFile(&myFile,                /* fsspec for the movie to be created */
  148.                             FOUR_CHAR_CODE('TVOD'),    /* creator value for the new file */
  149.                             smSystemScript,            /* script in which the movie file should be created */
  150.                             myFlags,                /* movie creation flags */
  151.                             &myResRefNum,            /* file reference for movie file return here */
  152.                             &myMovie);                /* new movie identifier return here */
  153.     if (myErr != noErr)
  154.         goto bail;
  155.     
  156.     // select the "no controller" movie controller option
  157.     myType = EndianU32_NtoB(myType);
  158.     SetUserDataItem(GetMovieUserData(myMovie),
  159.                     &myType,
  160.                     sizeof(myType),
  161.                     kUserDataMovieControllerType,
  162.                     1);
  163.     
  164.     //////////
  165.     //
  166.     // create the sprite track and media
  167.     //
  168.     //////////
  169.     
  170.     myTrack = NewMovieTrack(myMovie,
  171.                             ((long)kSpriteTrackWidth << 16),    /* display width in pixels */
  172.                             ((long)kSpriteTrackHeight << 16),    /* display height in pixels */
  173.                             kNoVolume);                            /* volume setting of the track */
  174.     myMedia = NewTrackMedia(myTrack,
  175.                             SpriteMediaType,        /* type of media to create */
  176.                             kSpriteMediaTimeScale,    /* media time scale */
  177.                             NULL,                    /* data ref, pass nil to use the file the movie was created in */
  178.                             0);                        /* data ref type, pass nil to use the file the movie was created in */
  179.  
  180.  
  181.     //////////
  182.     //
  183.     // create a key frame sample containing six sprites and all of their shared images
  184.     //
  185.     //////////
  186.  
  187.     // create a new, empty key frame sample
  188.     myErr = QTNewAtomContainer(&mySample);
  189.     if (myErr != noErr)
  190.         goto bail;
  191.  
  192.     myKeyColor.red = 0xffff;                        // white
  193.     myKeyColor.green = 0xffff;
  194.     myKeyColor.blue = 0xffff;
  195.  
  196.     // add images to the key frame sample
  197.     AddPICTImageToKeyFrameSample(mySample,                        /* the key frame sample */
  198.                                 kGoToBeginningButtonUp,            /* pict resource id */
  199.                                 &myKeyColor,                    /* RGBColor that should be used as the transparency color */
  200.                                 kGoToBeginningButtonUpIndex,    /* atom id for the kSpriteImageAtomType atom */
  201.                                 NULL,                            /* optional sprite registration point */
  202.                                 NULL);                            /* optional sprite image name */
  203.     AddPICTImageToKeyFrameSample(mySample, kGoToBeginningButtonDown, &myKeyColor, kGoToBeginningButtonDownIndex, NULL, NULL);
  204.     AddPICTImageToKeyFrameSample(mySample, kGoToEndButtonUp, &myKeyColor, kGoToEndButtonUpIndex, NULL, NULL);
  205.     AddPICTImageToKeyFrameSample(mySample, kGoToEndButtonDown, &myKeyColor, kGoToEndButtonDownIndex, NULL, NULL);
  206.     AddPICTImageToKeyFrameSample(mySample, kGoToPrevButtonUp, &myKeyColor, kGoToPrevButtonUpIndex, NULL, NULL);
  207.     AddPICTImageToKeyFrameSample(mySample, kGoToPrevButtonDown, &myKeyColor, kGoToPrevButtonDownIndex, NULL, NULL);
  208.     AddPICTImageToKeyFrameSample(mySample, kGoToNextButtonUp, &myKeyColor, kGoToNextButtonUpIndex, NULL, NULL);
  209.     AddPICTImageToKeyFrameSample(mySample, kGoToNextButtonDown, &myKeyColor, kGoToNextButtonDownIndex, NULL, NULL);
  210.     AddPICTImageToKeyFrameSample(mySample, kPenguinForward, &myKeyColor, kPenguinForwardIndex, NULL, NULL);
  211.     AddPICTImageToKeyFrameSample(mySample, kPenguinLeft, &myKeyColor, kPenguinLeftIndex, NULL, NULL);
  212.     AddPICTImageToKeyFrameSample(mySample, kPenguinRight, &myKeyColor, kPenguinRightIndex, NULL, NULL);
  213.     AddPICTImageToKeyFrameSample(mySample, kPenguinClosed, &myKeyColor, kPenguinClosedIndex, NULL, NULL);
  214.  
  215.     for (myIndex = kPenguinDownRightCycleStartIndex, myID = kWalkDownRightCycleStart; myIndex <= kPenguinDownRightCycleEndIndex; myIndex++, myID++)
  216.         AddPICTImageToKeyFrameSample(mySample, myID, &myKeyColor, myIndex, NULL, NULL);
  217.  
  218.  
  219.     /* assign group IDs to the images - you must assign group IDs to your sprite sample
  220.         if you want a sprite to display images with non-equivalent image descriptions 
  221.         (i.e., images with different dimensions) */
  222.     AssignImageGroupIDsToKeyFrame(mySample);
  223.  
  224.     
  225.     //////////
  226.     //
  227.     // add samples to the sprite track's media
  228.     //
  229.     //////////
  230.     
  231.     BeginMediaEdits(myMedia);
  232.  
  233.     // go to beginning button with no actions
  234.     myErr = QTNewAtomContainer(&myBeginButton);
  235.     if (myErr != noErr)
  236.         goto bail;
  237.     myLocation.h    = (1 * kSpriteTrackWidth / 8) - (kStartEndButtonWidth / 2);
  238.     myLocation.v    = (4 * kSpriteTrackHeight / 5) - (kStartEndButtonHeight / 2);
  239.     isVisible        = false;
  240.     myLayer            = 1;
  241.     myIndex            = kGoToBeginningButtonUpIndex;
  242.     myErr = SetSpriteData(myBeginButton,
  243.                         &myLocation,    /* sprite location */
  244.                         &isVisible,        /* sprite visible property */
  245.                         &myLayer,        /* sprite layer (smaller #'s are further forward) */
  246.                         &myIndex,        /* sprite index property */
  247.                         NULL,
  248.                         NULL,
  249.                         myActions);
  250.     if (myErr != noErr)
  251.         goto bail;
  252.  
  253.     // go to previous button with no actions
  254.     myErr = QTNewAtomContainer(&myPrevButton);
  255.     if (myErr != noErr)
  256.         goto bail;
  257.     myLocation.h     = (3 * kSpriteTrackWidth / 8) - (kNextPrevButtonWidth / 2);
  258.     myLocation.v    = (4 * kSpriteTrackHeight / 5) - (kStartEndButtonHeight / 2);
  259.     isVisible        = false;
  260.     myLayer            = 1;
  261.     myIndex            = kGoToPrevButtonUpIndex;
  262.     myErr = SetSpriteData(myPrevButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, myActions);
  263.     if (myErr != noErr)
  264.         goto bail;
  265.  
  266.     // go to next button with no actions
  267.     myErr = QTNewAtomContainer(&myNextButton);
  268.     if (myErr != noErr)
  269.         goto bail;
  270.     myLocation.h     = (5 * kSpriteTrackWidth / 8) - (kNextPrevButtonWidth / 2);
  271.     myLocation.v    = (4 * kSpriteTrackHeight / 5) - (kStartEndButtonHeight / 2);
  272.     isVisible        = false;
  273.     myLayer            = 1;
  274.     myIndex            = kGoToNextButtonUpIndex;
  275.     myErr = SetSpriteData(myNextButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, myActions);
  276.     if (myErr != noErr)
  277.         goto bail;
  278.  
  279.     // go to end button with no actions
  280.     myErr = QTNewAtomContainer(&myEndButton);
  281.     if (myErr != noErr)
  282.         goto bail;
  283.     myLocation.h     = (7 * kSpriteTrackWidth / 8) - (kStartEndButtonWidth / 2);
  284.     myLocation.v    = (4 * kSpriteTrackHeight / 5) - (kStartEndButtonHeight / 2);
  285.     isVisible        = false;
  286.     myLayer            = 1;
  287.     myIndex            = kGoToEndButtonUpIndex;
  288.     myErr = SetSpriteData(myEndButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, myActions);
  289.     if (myErr != noErr)
  290.         goto bail;
  291.  
  292.     // first penguin sprite with no actions
  293.     myErr = QTNewAtomContainer(&myPenguinOne);
  294.     if (myErr != noErr)
  295.         goto bail;
  296.     myLocation.h     = (3 * kSpriteTrackWidth / 8) - (kPenguinWidth / 2);
  297.     myLocation.v     = (kSpriteTrackHeight / 4) - (kPenguinHeight / 2);
  298.     isVisible        = true;
  299.     myLayer            = 2;
  300.     myIndex            = kPenguinDownRightCycleStartIndex;
  301.     myGraphicsMode.graphicsMode = blend;
  302.     myGraphicsMode.opColor.red = myGraphicsMode.opColor.green = myGraphicsMode.opColor.blue = 0x8FFF;    // grey
  303.     myErr = SetSpriteData(myPenguinOne, &myLocation, &isVisible, &myLayer, &myIndex, &myGraphicsMode, NULL, myActions);
  304.     if (myErr != noErr)
  305.         goto bail;
  306.         
  307.     // second penguin sprite with no actions
  308.     myErr = QTNewAtomContainer(&myPenguinTwo);
  309.     if (myErr != noErr)
  310.         goto bail;
  311.     myLocation.h     = (5 * kSpriteTrackWidth / 8) - (kPenguinWidth / 2);
  312.     myLocation.v     = (kSpriteTrackHeight / 4) - (kPenguinHeight / 2);
  313.     isVisible        = true;
  314.     myLayer            = 3;
  315.     myIndex            = kPenguinForwardIndex;
  316.     myErr = SetSpriteData(myPenguinTwo, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, myActions);
  317.     if (myErr != noErr)
  318.         goto bail;
  319.  
  320.  
  321.     //////////
  322.     //
  323.     // add actions to the six sprites
  324.     //
  325.     //////////
  326.  
  327.     // add go to beginning button
  328.     myErr = QTCopyAtom(myBeginButton, kParentAtomIsContainer, &myBeginActionButton);
  329.     if (myErr != noErr)
  330.         goto bail;
  331.  
  332.     AddSpriteSetImageIndexAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kGoToBeginningButtonDownIndex, NULL);
  333.     AddSpriteSetImageIndexAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kGoToBeginningButtonUpIndex, NULL);
  334.     AddMovieGoToBeginningAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
  335.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, true, NULL);
  336.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, false, NULL);
  337.     AddSpriteToSample(mySample, myBeginActionButton, kGoToBeginningSpriteID);    
  338.     QTDisposeAtomContainer(myBeginActionButton);
  339.  
  340.     // add go to prev button
  341.     myErr = QTCopyAtom(myPrevButton, kParentAtomIsContainer, &myPrevActionButton);
  342.     if (myErr != noErr)
  343.         goto bail;
  344.  
  345.     AddSpriteSetImageIndexAction(myPrevActionButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kGoToPrevButtonDownIndex, NULL);
  346.     AddSpriteSetImageIndexAction(myPrevActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kGoToPrevButtonUpIndex, NULL);
  347.     AddMovieStepBackwardAction(myPrevActionButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
  348.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, true, NULL);
  349.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, false, NULL);
  350.     AddSpriteToSample(mySample, myPrevActionButton, kGoToPrevSpriteID);
  351.     QTDisposeAtomContainer(myPrevActionButton);
  352.  
  353.     // add go to next button
  354.     myErr = QTCopyAtom(myNextButton, kParentAtomIsContainer, &myNextActionButton);
  355.     if (myErr != noErr)
  356.         goto bail;
  357.  
  358.     AddSpriteSetImageIndexAction(myNextActionButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kGoToNextButtonDownIndex, NULL);
  359.     AddSpriteSetImageIndexAction(myNextActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kGoToNextButtonUpIndex, NULL);
  360.     AddMovieStepForwardAction(myNextActionButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
  361.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, true, NULL);
  362.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, false, NULL);
  363.     AddSpriteToSample(mySample, myNextActionButton, kGoToNextSpriteID);
  364.     QTDisposeAtomContainer(myNextActionButton);
  365.  
  366.     // add go to end button
  367.     myErr = QTCopyAtom(myEndButton, kParentAtomIsContainer, &myEndActionButton);
  368.     if (myErr != noErr)
  369.         goto bail;
  370.  
  371.     AddSpriteSetImageIndexAction(myEndActionButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kGoToEndButtonDownIndex, NULL);
  372.     AddSpriteSetImageIndexAction(myEndActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kGoToEndButtonUpIndex, NULL);
  373.     AddMovieGoToEndAction(myEndActionButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
  374.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, true, NULL);
  375.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, false, NULL);
  376.     AddSpriteToSample(mySample, myEndActionButton, kGoToEndSpriteID);
  377.     QTDisposeAtomContainer(myEndActionButton);
  378.  
  379.     // add penguin one
  380.     myErr = QTCopyAtom(myPenguinOne, kParentAtomIsContainer, &myPenguinOneAction);
  381.     if (myErr != noErr)
  382.         goto bail;
  383.  
  384.     // show the buttons on mouse enter and hide them on mouse exit
  385.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, kTargetSpriteID, (void *)kGoToBeginningSpriteID, true, NULL);
  386.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, kTargetSpriteID, (void *)kGoToBeginningSpriteID, false, NULL);
  387.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, kTargetSpriteID, (void *)kGoToPrevSpriteID, true, NULL);
  388.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, kTargetSpriteID, (void *)kGoToPrevSpriteID, false, NULL);
  389.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, kTargetSpriteID, (void *)kGoToNextSpriteID, true, NULL);
  390.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, kTargetSpriteID, (void *)kGoToNextSpriteID, false, NULL);
  391.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, kTargetSpriteID, (void *)kGoToEndSpriteID, true, NULL);
  392.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, kTargetSpriteID, (void *)kGoToEndSpriteID, false, NULL);
  393.     AddSpriteToSample(mySample, myPenguinOneAction, kPenguinOneSpriteID);
  394.     QTWired_AddCursorChangeOnMouseOver(mySample, kPenguinOneSpriteID);
  395.     QTDisposeAtomContainer(myPenguinOneAction);
  396.  
  397.     // add penguin two
  398.     myErr = QTCopyAtom(myPenguinTwo, kParentAtomIsContainer, &myPenguinTwoAction);
  399.     if (myErr != noErr)
  400.         goto bail;
  401.  
  402.     // blink when clicked on
  403.     AddSpriteSetImageIndexAction(myPenguinTwoAction, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kPenguinClosedIndex, NULL);
  404.     AddSpriteSetImageIndexAction(myPenguinTwoAction, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kPenguinForwardIndex, NULL);
  405.  
  406.     AddQTEventAtom(myPenguinTwoAction, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton, &myEventAtom);
  407.  
  408.     // toggle the movie rate and both of the birds' graphics modes
  409.     QTWired_AddPenguinTwoConditionalActions(myPenguinTwoAction, myEventAtom);
  410.  
  411.     QTWired_AddWraparoundMatrixOnIdle(myPenguinTwoAction);
  412.  
  413.     AddSpriteToSample(mySample, myPenguinTwoAction, kPenguinTwoSpriteID);
  414.     QTDisposeAtomContainer(myPenguinTwoAction);
  415.     
  416.     // add an action for when the key frame is loaded, to set the movie's looping mode to palindrome;
  417.     // note that this will actually be triggered every time the key frame is reloaded,
  418.     // so if the operation was expensive we could use a conditional to test if we've already done it
  419.     myLoopingFlags = loopTimeBase | palindromeLoopTimeBase;
  420.     AddMovieSetLoopingFlagsAction(mySample, kParentAtomIsContainer, kQTEventFrameLoaded, myLoopingFlags);
  421.  
  422.  
  423.     // add the key frame sample to the sprite track media
  424.     //
  425.     // to add the sample data in a compressed form, you would use a QuickTime DataCodec to perform the
  426.     // compression; replace the call to the utility AddSpriteSampleToMedia with a call to the utility
  427.     // AddCompressedSpriteSampleToMedia to do this
  428.     
  429.     AddSpriteSampleToMedia(myMedia, mySample, kSpriteMediaFrameDuration, true, NULL);    
  430.  
  431.  
  432.     //////////
  433.     //
  434.     // add a few override samples to move penguin one and change its image index
  435.     //
  436.     //////////
  437.  
  438.     // original penguin one location
  439.     myLocation.h     = (3 * kSpriteTrackWidth / 8) - (kPenguinWidth / 2);
  440.     myLocation.v     = (kSpriteTrackHeight / 4) - (kPenguinHeight / 2);
  441.  
  442.     myDelta = (kSpriteTrackHeight / 2) / kNumOverrideSamples;
  443.     myIndex = kPenguinDownRightCycleStartIndex;
  444.     
  445.     for (i = 1; i <= kNumOverrideSamples; i++) {
  446.         QTRemoveChildren(mySample, kParentAtomIsContainer);
  447.         QTNewAtomContainer(&myPenguinOneOverride);
  448.  
  449.         myLocation.h += myDelta;
  450.         myLocation.v += myDelta;
  451.         myIndex++;
  452.         if (myIndex > kPenguinDownRightCycleEndIndex)
  453.             myIndex = kPenguinDownRightCycleStartIndex;
  454.             
  455.         SetSpriteData(myPenguinOneOverride, &myLocation, NULL, NULL, &myIndex, NULL, NULL, NULL);
  456.         AddSpriteToSample(mySample, myPenguinOneOverride, kPenguinOneSpriteID);
  457.         AddSpriteSampleToMedia(myMedia, mySample, kSpriteMediaFrameDuration, false, NULL);    
  458.         QTDisposeAtomContainer(myPenguinOneOverride);
  459.     }
  460.  
  461.     EndMediaEdits(myMedia);
  462.  
  463.  
  464.     // add the media to the track
  465.     InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
  466.  
  467.     
  468.     //////////
  469.     //
  470.     // set the sprite track properties
  471.     //
  472.     //////////
  473.     {
  474.         QTAtomContainer        myTrackProperties;
  475.         RGBColor            myBackgroundColor;
  476.         
  477.         // add a background color to the sprite track
  478.         myBackgroundColor.red = EndianU16_NtoB(0x8000);
  479.         myBackgroundColor.green = EndianU16_NtoB(0);
  480.         myBackgroundColor.blue = EndianU16_NtoB(0xffff);
  481.         
  482.         QTNewAtomContainer(&myTrackProperties);
  483.         QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyBackgroundColor, 1, 1, sizeof(RGBColor), &myBackgroundColor, NULL);
  484.  
  485.         // tell the movie controller that this sprite track has actions, Jackson
  486.         hasActions = true;
  487.         QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyHasActions, 1, 1, sizeof(hasActions), &hasActions, NULL);
  488.     
  489.         // tell the sprite track to generate QTIdleEvents
  490.         myFrequency = EndianU32_NtoB(60);
  491.         QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyQTIdleEventsFrequency, 1, 1, sizeof(myFrequency), &myFrequency, NULL);
  492.         myErr = SetMediaPropertyAtom(myMedia, myTrackProperties);
  493.         if (myErr != noErr)
  494.             goto bail;
  495.  
  496.         QTDisposeAtomContainer(myTrackProperties);
  497.     }
  498.  
  499.  
  500.     //////////
  501.     //
  502.     // finish up
  503.     //
  504.     //////////
  505.     
  506.     // add the movie resource to the movie file
  507.     myErr = AddMovieResource(myMovie, myResRefNum, &myResID, myFile.name);
  508.     
  509. bail:
  510.     free(myPrompt);
  511.     free(myFileName);
  512.  
  513.     if (mySample != NULL)
  514.         QTDisposeAtomContainer(mySample);
  515.  
  516.     if (myBeginButton != NULL)
  517.         QTDisposeAtomContainer(myBeginButton);    
  518.             
  519.     if (myPrevButton != NULL)
  520.         QTDisposeAtomContainer(myPrevButton);
  521.                 
  522.     if (myNextButton != NULL)
  523.         QTDisposeAtomContainer(myNextButton);
  524.                 
  525.     if (myEndButton != NULL)
  526.         QTDisposeAtomContainer(myEndButton);        
  527.         
  528.     if (myResRefNum != 0)
  529.         CloseMovieFile(myResRefNum);
  530.  
  531.     if (myMovie != NULL)
  532.         DisposeMovie(myMovie);
  533.         
  534.     return(myErr);
  535. }
  536.  
  537.  
  538. //////////
  539. //
  540. // QTWired_AddPenguinTwoConditionalActions
  541. // Add actions to the second penguin that transform him (her?) into a two state button
  542. // that plays or pauses the movie.
  543. //
  544. // We are relying on the fact that a "GetVariable" for a variable ID which has never been set
  545. // will return zero. If we needed a different default value, we could initialize it using the
  546. // frameLoaded event.
  547. //
  548. // A higher-level description of the logic is:
  549. // 
  550. //     On MouseUpInside
  551. //        If (GetVariable(DefaultTrack, 1) = 0)
  552. //           SetMovieRate(1)
  553. //           SetSpriteGraphicsMode(DefaultSprite, { blend, grey } )
  554. //           SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5), { ditherCopy, white } )
  555. //           SetVariable(DefaultTrack, 1, 1)
  556. //        ElseIf (GetVariable(DefaultTrack, 1) = 1)
  557. //           SetMovieRate(0)
  558. //           SetSpriteGraphicsMode(DefaultSprite, { ditherCopy, white })
  559. //           SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5), { blend, grey })
  560. //           SetVariable(DefaultTrack, 1, 0)
  561. //        Endif
  562. //     End
  563. // 
  564. //////////
  565.  
  566. OSErr QTWired_AddPenguinTwoConditionalActions (QTAtomContainer theContainer, QTAtom theEventAtom)
  567. {
  568.     QTAtom                                myNewActionAtom, myNewParamAtom, myConditionalAtom;
  569.     QTAtom                                myExpressionAtom, myOperatorAtom, myActionListAtom;
  570.     short                                myParamIndex, myConditionIndex, myOperandIndex;
  571.     float                                myConstantValue;
  572.     QTAtomID                            myVariableID;
  573.     ModifierTrackGraphicsModeRecord        myBlendMode, myCopyMode;
  574.     
  575.     myBlendMode.graphicsMode = blend;
  576.     myBlendMode.opColor.red = myBlendMode.opColor.green = myBlendMode.opColor.blue = 0x8fff;    // grey
  577.  
  578.     myCopyMode.graphicsMode = ditherCopy;
  579.     myCopyMode.opColor.red = myCopyMode.opColor.green = myCopyMode.opColor.blue = 0xffff;        // white
  580.  
  581.     AddActionAtom(theContainer, theEventAtom, kActionCase, &myNewActionAtom);
  582.     
  583.     myParamIndex = 1;
  584.     AddActionParameterAtom(theContainer, myNewActionAtom, myParamIndex, 0, NULL, &myNewParamAtom);
  585.  
  586.     // first condition
  587.     myConditionIndex = 1;
  588.     AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex, &myConditionalAtom);
  589.     AddExpressionContainerAtomType(theContainer, myConditionalAtom, &myExpressionAtom);
  590.     AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo, &myOperatorAtom);
  591.  
  592.     myOperandIndex = 1;
  593.     myConstantValue = kButtonStateOne;
  594.     AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant, myOperandIndex, NULL, myConstantValue);
  595.  
  596.     myOperandIndex = 2;
  597.     myVariableID = kPenguinStateVariableID;
  598.     AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex, 0, NULL, 0, myVariableID);
  599.  
  600.     AddActionListAtom(theContainer, myConditionalAtom, &myActionListAtom);
  601.     AddMovieSetRateAction(theContainer, myActionListAtom, 0, Long2Fix(1));
  602.     AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0, NULL, 0, 0, NULL, &myBlendMode, NULL);
  603.     AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0, NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID, &myCopyMode, NULL);
  604.     AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0, kPenguinStateVariableID, kButtonStateTwo, 0, NULL, 0);
  605.                                        
  606.     // second condition
  607.     myConditionIndex = 2;
  608.     AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex, &myConditionalAtom);
  609.     AddExpressionContainerAtomType(theContainer, myConditionalAtom, &myExpressionAtom);
  610.     AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo, &myOperatorAtom);
  611.  
  612.     myOperandIndex = 1;
  613.     myConstantValue = kButtonStateTwo;
  614.     AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant, myOperandIndex, NULL, myConstantValue);
  615.  
  616.     myOperandIndex = 2;
  617.     myVariableID = kPenguinStateVariableID;
  618.     AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex, 0, NULL, 0, myVariableID);
  619.  
  620.     AddActionListAtom(theContainer, myConditionalAtom, &myActionListAtom);
  621.     AddMovieSetRateAction(theContainer, myActionListAtom, 0, Long2Fix(0));
  622.     AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0, NULL, 0, 0, NULL, &myCopyMode, NULL);
  623.     AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0, NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID, &myBlendMode, NULL);
  624.     AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0, kPenguinStateVariableID, kButtonStateOne, 0, NULL, 0);
  625.  
  626.     return(noErr);
  627. }
  628.  
  629.  
  630. //////////
  631. //
  632. // QTWired_AddWraparoundMatrixOnIdle
  633. // Add beginning, end, and change matrices to the specified atom container.
  634. //
  635. //////////
  636.  
  637. OSErr QTWired_AddWraparoundMatrixOnIdle (QTAtomContainer theContainer)
  638. {
  639.     MatrixRecord     myMinMatrix, myMaxMatrix, myDeltaMatrix;
  640.     long            myFlags = kActionFlagActionIsDelta | kActionFlagParameterWrapsAround;
  641.     QTAtom            myActionAtom;
  642.     OSErr            myErr = noErr;
  643.     
  644.     myMinMatrix.matrix[0][0] = myMinMatrix.matrix[0][1] = myMinMatrix.matrix[0][2] = EndianS32_NtoB(0xffffffff);
  645.     myMinMatrix.matrix[1][0] = myMinMatrix.matrix[1][1] = myMinMatrix.matrix[1][2] = EndianS32_NtoB(0xffffffff);
  646.     myMinMatrix.matrix[2][0] = myMinMatrix.matrix[2][1] = myMinMatrix.matrix[2][2] = EndianS32_NtoB(0xffffffff);
  647.  
  648.     myMaxMatrix.matrix[0][0] = myMaxMatrix.matrix[0][1] = myMaxMatrix.matrix[0][2] = EndianS32_NtoB(0x7fffffff);
  649.     myMaxMatrix.matrix[1][0] = myMaxMatrix.matrix[1][1] = myMaxMatrix.matrix[1][2] = EndianS32_NtoB(0x7fffffff);
  650.     myMaxMatrix.matrix[2][0] = myMaxMatrix.matrix[2][1] = myMaxMatrix.matrix[2][2] = EndianS32_NtoB(0x7fffffff);
  651.     
  652.     myMinMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((1 * kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));
  653.     myMaxMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((3 * kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));
  654.  
  655.     SetIdentityMatrix(&myDeltaMatrix);
  656.     myDeltaMatrix.matrix[2][1] = Long2Fix(1);
  657.     
  658.     // change location
  659.     myErr = AddSpriteSetMatrixAction(theContainer, kParentAtomIsContainer, kQTEventIdle, 0, NULL, 0, 0, NULL, &myDeltaMatrix, &myActionAtom);
  660.     if (myErr != noErr)
  661.         goto bail;
  662.  
  663.     myErr = AddActionParameterOptions(theContainer, myActionAtom, 1, myFlags, sizeof(myMinMatrix), &myMinMatrix, sizeof(myMaxMatrix), &myMaxMatrix);
  664.  
  665. bail:
  666.     return(myErr);
  667. }
  668.  
  669.  
  670. //////////
  671. //
  672. // QTWired_AddCursorChangeOnMouseOver
  673. // Add a cursor-change-on-mouse-over action to the sprite having the specified ID
  674. // in the specified atom container.
  675. //
  676. // Here we use the new "sprite behaviors atom" introduced in QuickTime 4.0, which
  677. // simplifies adding button-like capabilities to sprites.
  678. //
  679. //////////
  680.  
  681. OSErr QTWired_AddCursorChangeOnMouseOver (QTAtomContainer theContainer, QTAtomID theID)
  682. {
  683.     QTAtom                                mySpriteAtom = 0;
  684.     QTAtom                                myBehaviorAtom = 0;
  685.     QTSpriteButtonBehaviorStruct        myBehaviorRec;
  686.     OSErr                                myErr = noErr;
  687.     
  688.     // find the sprite atom with the specified ID in the specified container
  689.     mySpriteAtom = QTFindChildByID(theContainer, kParentAtomIsContainer, kSpriteAtomType, theID, NULL);
  690.     if (mySpriteAtom == 0) {
  691.         // if there is none, insert a new sprite atom into the specified container
  692.         myErr = QTInsertChild(theContainer, kParentAtomIsContainer, kSpriteAtomType, theID, 0, 0, NULL, &mySpriteAtom);
  693.         if (myErr != noErr)
  694.             goto bail;
  695.     }
  696.     
  697.     // insert a new sprite behaviors atom into the sprite atom
  698.     myErr = QTInsertChild(theContainer, mySpriteAtom, kSpriteBehaviorsAtomType, 1, 1, 0, NULL, &myBehaviorAtom);
  699.     if (myErr != noErr)
  700.         goto bail;
  701.  
  702.     //////////
  703.     //
  704.     // insert three atoms into the sprite behaviors atom; these three atoms specify what to do on each
  705.     // of the four defined state transitions for the (1) sprite image, (2) cursor, and (3) status string
  706.     //
  707.     //////////
  708.     
  709.     // set the sprite image behavior; -1 means: no change associated with this state transition
  710.     myBehaviorRec.notOverNotPressedStateID = EndianS32_NtoB(-1);
  711.     myBehaviorRec.overNotPressedStateID = EndianS32_NtoB(-1);
  712.     myBehaviorRec.overPressedStateID = EndianS32_NtoB(-1);
  713.     myBehaviorRec.notOverPressedStateID = EndianS32_NtoB(-1);
  714.  
  715.     myErr = QTInsertChild(theContainer, myBehaviorAtom, kSpriteImageBehaviorAtomType, 1, 1, sizeof(QTSpriteButtonBehaviorStruct), &myBehaviorRec, NULL);
  716.     if (myErr != noErr)
  717.         goto bail;
  718.     
  719.     // set the sprite cursor behavior; -1 means: no change associated with this state transition
  720.     myBehaviorRec.notOverNotPressedStateID = EndianS32_NtoB(-1);
  721.     myBehaviorRec.overNotPressedStateID = EndianS32_NtoB(kQTCursorOpenHand);
  722.     myBehaviorRec.overPressedStateID = EndianS32_NtoB(-1);
  723.     myBehaviorRec.notOverPressedStateID = EndianS32_NtoB(-1);
  724.  
  725.     myErr = QTInsertChild(theContainer, myBehaviorAtom, kSpriteCursorBehaviorAtomType, 1, 1, sizeof(QTSpriteButtonBehaviorStruct), &myBehaviorRec, NULL);
  726.     if (myErr != noErr)
  727.         goto bail;
  728.     
  729.     // set the status string behavior; -1 means: no change associated with this state transition
  730.     myBehaviorRec.notOverNotPressedStateID = EndianS32_NtoB(-1);
  731.     myBehaviorRec.overNotPressedStateID = EndianS32_NtoB(-1);
  732.     myBehaviorRec.overPressedStateID = EndianS32_NtoB(-1);
  733.     myBehaviorRec.notOverPressedStateID = EndianS32_NtoB(-1);
  734.  
  735.     myErr = QTInsertChild(theContainer, myBehaviorAtom, kSpriteStatusStringsBehaviorAtomType, 1, 1, sizeof(QTSpriteButtonBehaviorStruct), &myBehaviorRec, NULL);
  736.     if (myErr != noErr)
  737.         goto bail;
  738.             
  739. bail:
  740.     return(myErr);
  741. }
  742.